package com.sndscl.rpc.call;

import com.sndscl.rpc.common.constant.SndSclConstant;
import com.sndscl.rpc.common.dto.RpcRequest;
import com.sndscl.rpc.common.rpc.RequestEncoder;
import com.sndscl.rpc.common.rpc.ResponseDecoder;
import io.netty.bootstrap.Bootstrap;
import io.netty.buffer.Unpooled;
import io.netty.channel.*;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioSocketChannel;
import io.netty.handler.codec.DelimiterBasedFrameDecoder;
import io.netty.handler.timeout.IdleStateHandler;

/**
 * @Description
 * @Author lsh
 * @Date 2020/10/23 15:30
 **/
public class NettyClient {
    private String ip;
    private int port;
    private String key;
    private long reConNum = 0;
    private Channel channel;
    private EventLoopGroup loopGroup = new NioEventLoopGroup();
    private Bootstrap bootstrap = new Bootstrap();

    public NettyClient(String ip,int port,String key){
        this.ip = ip;
        this.port = port;
        this.key = key;
    }

    public void doConnection(){
        bootstrap.group(loopGroup)
                .channel(NioSocketChannel.class)
                .handler(new ChannelInitializer<Channel>() {
                    @Override
                    protected void initChannel(Channel channel) throws Exception {
                        ChannelPipeline channelPipeline = channel.pipeline();
                        channelPipeline
                                .addLast("delimiter", new DelimiterBasedFrameDecoder(3145728,true,
                                        Unpooled.copiedBuffer(SndSclConstant.NETTY_DELIMITER.getBytes())))
                                .addLast(new IdleStateHandler(0,0,5))
                                .addLast(new RequestEncoder())
                                .addLast(new ResponseDecoder())
                                .addLast(new ResponseHandler(NettyClient.this));
                    }
                });
        reConnection();
    }

    public void reConnection(){
        try {
            if(reConNum > 100){
                this.shutdown();
                return;
            }
            ChannelFuture future = bootstrap.connect(ip,port).sync();
            future.addListener(new ChannelFutureListener() {
                @Override
                public void operationComplete(ChannelFuture channelFuture) throws Exception {
                    if(channelFuture.isSuccess()){
                        System.out.println("连接服务器成功！");
                        channel = channelFuture.channel();
                        startPing();
                        registerClient();
                        reConNum = 0;
                    }else{
                        System.out.println("连接服务器失败！");
                        //重连
                        reConnection();
                        reConNum++;
                        Thread.sleep(1000);
                    }
                }
            });
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    public void timeOutReConnection(){
        SndSclCall.moveToReConnectionMap(key);
        reConnection();
    }



    private void registerClient(){
        SndSclCall.registerClient(key,this);
    }

    public synchronized void send(Object object){
        if(channel.isActive()){
            channel.writeAndFlush(object);
            channel.writeAndFlush(Unpooled.copiedBuffer(SndSclConstant.NETTY_DELIMITER.getBytes()));
        }
    }

    public void shutdown(){
        loopGroup.shutdownGracefully();
        SndSclCall.removeClient(key);
        SndSclCall.removemoveToReConnectionMap(key);
    }

    public void startPing(){
        new Thread(()->{
            while(true){
                RpcRequest rpcRequest = new RpcRequest();
                rpcRequest.setType("00");
                send(rpcRequest);
                try {
                    Thread.sleep(500);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                if(!channel.isActive()){
                    break;
                }

            }
        }).start();
    }

    public boolean channelIsActive(){
        return channel.isActive();
    }

    public String getIp() {
        return ip;
    }

    public int getPort() {
        return port;
    }

    public String getKey() {
        return key;
    }
}
